home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume19 / backup next >
Encoding:
Internet Message Format  |  1989-06-29  |  52.6 KB

  1. Subject:  v19i102:  Save files that have changed in the last little while
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Rayan Zachariassen <rayan@ai.toronto.edu>
  7. Posting-number: Volume 19, Issue 102
  8. Archive-name: backup
  9.  
  10. The backup program is an online disk-to-disk incremental backup utility
  11. that maintains a file queue in a seperate partition, copying files that
  12. have been modified since its last run to the special backup filesystem.
  13. As space is needed on the backup partition, just enough of the oldest files
  14. are removed to make space for new file copies.  In other words, a fixed
  15. size (in disk blocks) queue of recently modified files.
  16.  
  17. Assumes SunOS filesystem interface (vnodes, etc.), would not be hard to
  18. port to straight BSD filesystem.
  19.  
  20. #! /bin/sh
  21. # This is a shell archive.  Remove anything before this line, then unpack
  22. # it by saving it into a file and typing "sh file".  To overwrite existing
  23. # If this archive is complete, you will see the following message at the end:
  24. #        "End of shell archive."
  25. #
  26. # Contents:
  27. #   README Makefile backup.8 backup.c backup.conf
  28. #   backup.filter getback.1 getback.c getback.sh ilw.c
  29. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  30. if test -f README -a "${1}" != "-c" ; then 
  31.   echo shar: Will not over-write existing file \"README\"
  32. else
  33. echo shar: Extracting \"README\" \(1083 characters\)
  34. sed "s/^X//" >README <<'END_OF_README'
  35. XThe backup program is an online disk-to-disk incremental backup utility
  36. Xthat maintains a file queue in a seperate partition, copying files that
  37. Xhave been modified since its last run to the special backup filesystem.
  38. XAs space is needed on the backup partition, just enough of the oldest files
  39. Xare removed to make space for new file copies.  In other words, a fixed
  40. Xsize (in disk blocks) queue of recently modified files.
  41. X
  42. X
  43. XDefaults, overridden by command line arguments:
  44. X
  45. X    BACKUP        /backup
  46. X    CONFIG        /etc/backup.conf
  47. X    MAXSIZE        100k
  48. X    DELTA        24hr
  49. X
  50. XAssumes SunOS filesystem interface (vnodes, etc.), would not
  51. Xbe hard to port to straight BSD filesystem.
  52. X
  53. XInstallation:
  54. X
  55. X- Edit Makefile.
  56. X- Tweak default parameters in backup.c if you don't like the above values.
  57. X- Create and install a configuration file, see the example in ./backup.conf.
  58. X- make; make install
  59. X- Allocate a /backup partition, create and mount an empty filesystem on it.
  60. X- Add a crontab entry, e.g.:
  61. X    37 4,12,20 * * * /local/etc/backup
  62. X
  63. XThanks to Ken Lalonde for code tweaks, getback.sh, and most of this README file
  64. X
  65. Xrayan
  66. END_OF_README
  67. if test 1083 -ne `wc -c <README`; then
  68.     echo shar: \"README\" unpacked with wrong size!
  69. fi
  70. # end of overwriting check
  71. fi
  72. if test -f Makefile -a "${1}" != "-c" ; then 
  73.   echo shar: Will not over-write existing file \"Makefile\"
  74. else
  75. echo shar: Extracting \"Makefile\" \(549 characters\)
  76. sed "s/^X//" >Makefile <<'END_OF_Makefile'
  77. XDESTDIR=/local
  78. XCOPTS = # -Bstatic
  79. XCFLAGS = $(COPTS) -DCONFIG=\"$(DESTDIR)/etc/backup.conf\"
  80. X
  81. Xall:    backup getback
  82. X
  83. Xbackup:    backup.o ilw.o
  84. X    $(CC) $(CFLAGS) -o backup backup.o ilw.o
  85. X
  86. Xinstall: all
  87. X    install -m 710 backup $(DESTDIR)/etc/backup
  88. X    install -m 755 getback $(DESTDIR)/bin/getback
  89. X    install -c -m 644 backup.8 $(DESTDIR)/man/man8
  90. X    install -c -m 644 getback.1 $(DESTDIR)/man/man1
  91. X
  92. Xclean:
  93. X    rm -f *.o backup getback make.log
  94. X
  95. Xdist:
  96. X    shar README Makefile backup.8 backup.c backup.conf backup.filter getback.1 getback.c getback.sh ilw.c > backup.shar
  97. END_OF_Makefile
  98. if test 549 -ne `wc -c <Makefile`; then
  99.     echo shar: \"Makefile\" unpacked with wrong size!
  100. fi
  101. # end of overwriting check
  102. fi
  103. if test -f backup.8 -a "${1}" != "-c" ; then 
  104.   echo shar: Will not over-write existing file \"backup.8\"
  105. else
  106. echo shar: Extracting \"backup.8\" \(4871 characters\)
  107. sed "s/^X//" >backup.8 <<'END_OF_backup.8'
  108. X.TH BACKUP 8 "UofToronto"
  109. X.SH NAME
  110. Xbackup \- save files that have changed in the last little while
  111. X.SH SYNOPSIS
  112. X.B backup
  113. X[ -devz
  114. X.RI -b #
  115. X] [ -c
  116. X.I backup.conf
  117. X] [ -o
  118. X.I /backup
  119. X] [
  120. X.I /path
  121. X\&... ]
  122. X.SH DESCRIPTION
  123. X.I Backup
  124. Xcopies to a special filesystem hierarchy all files which have been modified
  125. Xsince the previous run of
  126. X.I backup
  127. Xon that subtree of the filesystem.
  128. XThe subtree(s) may be specified explicitly on the command line.
  129. XIf none are given, a configuration file (default is
  130. X.BR /local/etc/backup.conf )
  131. Xis read to determine which subtrees to scan for recently changed files.
  132. XIf subtrees are explicitly listed, a run is forced.  Otherwise,
  133. X.I backup
  134. Xwill only do its deed if the subtree is due for backing up as indicated
  135. Xby the interval time, and the last backup scan time, in the configuration
  136. Xfile.  If the subtree is not found in the configuration file, all files
  137. Xchanged during the last day will be selected.
  138. X.PP
  139. XThe format of the configuration file is:
  140. X.IP -
  141. Xlines starting with
  142. X.B #
  143. Xare ignored.
  144. X.IP -
  145. Xall other lines contain four space-separated fields:
  146. X.IP
  147. XField 1 is the path name of a subtree to back up.
  148. X.IP
  149. XField 2 is the desired interval between backups.
  150. X.IP
  151. XField 3 is the name of a filter file containing glob patterns,
  152. Xone per line, describing names of files to
  153. X.I ignore
  154. Xwhen scanning the subtree.
  155. X.IP
  156. XField 4 is a timestamp in the exact syntax of
  157. X.BR ctime (3).
  158. X.PP
  159. XThis is a sample configuration file:
  160. X.ta 1.7i,2.3i,3.7i
  161. X.nf
  162. X
  163. X# path    intvl    filter file    last done
  164. X/usr    8h    /dev/null    Sat Apr 30 00:33:03 1988
  165. X/homes/neat/car    8h    /etc/filter.u    Sat Apr 30 00:33:03 1988
  166. X/homes/neat/cdr    8h    /etc/filter.u    Sat Apr 30 00:33:03 1988
  167. X/var    24h    /dev/null    Sat Apr 30 00:33:03 1988
  168. X.fi
  169. X.PP
  170. XThe first three fields are maintained manually (and defaulted if
  171. X.I backup
  172. Xis given a subtree argument not appearing in the configuration file).
  173. XThe last field is maintained by the program itself.  After every successful
  174. Xrun, the configuration file is written out containing the updated timestamp
  175. Xinformation.  The purpose of the configuration file is to allow all information
  176. Xbe maintained in this form, and the
  177. X.I backup
  178. Xprogram started very frequently by
  179. X.BR cron (8).
  180. X.PP
  181. XBacked up files are stored in a parallel directory hierarchy under the
  182. Xroot of the backup hierarchy (by default
  183. X.BR /backup ).
  184. XEach backed up file has associated with it a directory whose relative
  185. Xpathname under the root of the backup hierarchy is its full pathname.
  186. XThis directory contains one or more backed up files in files named by
  187. Xthe date of the backup.  Thus, a user with a file
  188. X.br
  189. X.sp
  190. X.I /homes/neat/car/user/src/stuff.c
  191. X.br
  192. X.sp
  193. Xthat he has been working on and hence has been backed up a number
  194. Xof times might find a set of files
  195. X.br
  196. X.sp
  197. X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-06:05 ,
  198. X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-09:05 ,
  199. X.br
  200. X.sp
  201. Xand
  202. X.br
  203. X.sp
  204. X.IR /backup/homes/neat/car/user/src/stuff.c/Apr30-12:05 .
  205. X.br
  206. X.sp
  207. XPermissions and owner/group are copied with the files.
  208. X.PP
  209. XAs the backups are very expensive in terms of file space only
  210. Xfiles of less than some size, currently 100 kilobytes, are saved.
  211. XFiles can be deselected by creating a filter file, and adding to it
  212. Xglob patterns that would match suspicious names.   Typically,
  213. Xfor example
  214. X.IR core ,
  215. X.IR a.out ,
  216. Xemacs temporaries like
  217. X.I #*
  218. Xor
  219. X.I *~
  220. Xand many others, are not backed up.
  221. X.PP
  222. XWhen filespace usage on the backup root's file system passes a high water mark,
  223. Xcurrently 95%,
  224. X.I
  225. Xbackup
  226. Xdeletes files in order by modification date until it has enough space under
  227. Xthe high water mark to add new backup files.
  228. X.PP
  229. X.I Backup
  230. Xreads the entire i-list for the filesystem, saving information such as the
  231. Xmodification time and type of the inode.  Thereafter all the appropriate
  232. Xfile names are identified by tree walks in the filesystem, and backed up.
  233. XIf files must be deleted from the backup hierarchy,
  234. Xthen it does the same for the backup subtree (only deleting instead of
  235. Xbackup up!).
  236. X.PP
  237. XThe various options are:
  238. X.IP -b
  239. Xthe argument is the largest size in kilobytes of files to consider backing up.
  240. X.IP -c
  241. Xspecifies an alternate configuration file.
  242. X.IP -d
  243. Xturns a little bit of debugging output on.
  244. X.IP -e
  245. Xwill not ignore executable files when looking for files to back up.
  246. X.IP -o
  247. Xspecifies an alternate backup hierarchy to place backed up files.
  248. X.IP -v
  249. Xturns on verbosity, and can be doubled for more chatter.
  250. X.IP -z
  251. Xasks the program to go through the motions, but to not take any action
  252. Xwhatsoever.
  253. X.SH FILES
  254. X.ta 2.5i
  255. X/backup    - mounted filesystem for backups
  256. X.br
  257. X/local/etc/backup.conf    - backup configuration information
  258. X.SH INFO
  259. XOriginal idea by Ciaran O'Donnell of U of Waterloo, whose version was
  260. Xwritten in 1975.  This is an independent reimplementation native to the
  261. XSunOS environment, written by Rayan Zachariassen at U of Toronto
  262. X(bug fixes and improvements to <rayan@ai.toronto.edu>).
  263. END_OF_backup.8
  264. if test 4871 -ne `wc -c <backup.8`; then
  265.     echo shar: \"backup.8\" unpacked with wrong size!
  266. fi
  267. # end of overwriting check
  268. fi
  269. if test -f backup.c -a "${1}" != "-c" ; then 
  270.   echo shar: Will not over-write existing file \"backup.c\"
  271. else
  272. echo shar: Extracting \"backup.c\" \(28044 characters\)
  273. sed "s/^X//" >backup.c <<'END_OF_backup.c'
  274. X/*
  275. X * backup - do incremental disk-to-disk backups of individual files
  276. X *
  277. X * This code is a complete reimplementation (for the SunOS environment)
  278. X * of an original idea by Ciaran O'Donnell.  This implementation is
  279. X * Copyright 1988 by Rayan Zachariassen, solely to prevent you selling
  280. X * it or putting your name on it.  Free (gratis) redistribution is
  281. X * encouraged.
  282. X */
  283. X
  284. X#ifndef    lint
  285. Xstatic char *RCSid = "$Header: /ai/car/src/etc/backup/RCS/backup.c,v 1.2 89/05/23 22:13:19 rayan Exp $";
  286. X#endif    lint
  287. X
  288. X#include <stdio.h>
  289. X#include <ctype.h>
  290. X#include <mntent.h>
  291. X#include <values.h>
  292. X#include <errno.h>
  293. X#include <sys/param.h>
  294. X#include <sys/types.h>
  295. X#include <sys/file.h>
  296. X#include <sys/stat.h>
  297. X#include <sys/time.h>
  298. X#include <sys/vnode.h>
  299. X#include <sys/vfs.h>
  300. X#include <ufs/inode.h>
  301. X#include <ufs/fs.h>
  302. X#include <sys/dir.h>
  303. X
  304. X#define    HIWATER        95    /* % of backup filesystem used at high water */
  305. X#define    LOWATER        85    /* % of backup filesystem used at low water */
  306. X
  307. X#define    BACKUP        "/backup"    /* backup filesystem mount point */
  308. X#ifndef    CONFIG
  309. X#define    CONFIG        "/etc/backup.conf"    /* configuration file */
  310. X#endif    /* !CONFIG */
  311. X
  312. X#define    MAXSIZE        100000    /* default maximum size of files to back up */
  313. X
  314. X#define    DELTA        (24*60*60)    /* default time since last backup */
  315. X
  316. Xint    maxsize = MAXSIZE;    /* maximum size in bytes of files to back up */
  317. Xint    doexecs = 0;        /* should we back up executables? */
  318. X
  319. Xstruct a_path {
  320. X    char        *path;    /* path to tree hierarchy we want to back up */
  321. X    char        *glob;    /* pointer to the contents of the filter file */
  322. X    time_t        newer;    /* back up files newer than this time */
  323. X    struct config    *config;/* back pointer to config structure */
  324. X    struct a_path    *next;
  325. X};
  326. X
  327. Xstruct a_dev {
  328. X    char        *text;    /* raw device name */
  329. X    struct a_path    *paths;    /* pointer to list of paths */
  330. X    struct a_dev    *next;    /* pointer to next device */
  331. X};
  332. X
  333. Xstruct a_dev    *devlist = NULL;
  334. X
  335. Xstruct config {
  336. X    char    *path;        /* top of hierarchy we want to back up */
  337. X    char    *interval;    /* how often to do incrementals */
  338. X    char    *filter;    /* name of file containing regexp's */
  339. X    char    *line;        /* the config file line excluding time field */
  340. X    time_t    lasttime;    /* last time incrementals were done here */
  341. X    struct config    *next;
  342. X};
  343. X
  344. Xchar    *progname, *backup, *devbackup, *backupmnt;
  345. Xint    debug, verbose, dryrun;
  346. Xtime_t    now, lasttime;
  347. X
  348. Xextern int    optind;
  349. Xextern char    *optarg;
  350. Xextern char    *emalloc();
  351. X
  352. X#define    EMSG(x)    (errno < sys_nerr ? sys_errlist[x] : \
  353. X           (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
  354. X
  355. Xextern int    errno, sys_nerr;
  356. Xextern char    *sys_errlist[];
  357. Xchar        errmsgbuf[30];
  358. X
  359. Xmain(argc, argv)
  360. X    int    argc;
  361. X    char    *argv[];
  362. X{
  363. X    int        c, errflag;
  364. X    char        *cffile;
  365. X    struct a_dev    *lp;
  366. X    struct a_path    *pp;
  367. X    struct config    *cf, *cfp;
  368. X    FILE        *cf_fp;
  369. X    extern time_t time();
  370. X    extern char *getdevice(), *getpath(), *ctime();
  371. X    extern struct config *readconfig();
  372. X    extern void writeconfig();
  373. X
  374. X    progname = argv[0];
  375. X    errflag = 0;
  376. X    dryrun = 0;
  377. X    debug = verbose = 0;
  378. X    backup = BACKUP;
  379. X    cffile = CONFIG;
  380. X    while ((c = getopt(argc, argv, "bc:devo:z")) != EOF) {
  381. X        switch (c) {
  382. X        case 'b':    /* don't back up files larger than arg kbytes */
  383. X            if ((maxsize = atoi(optarg)) < 1) {
  384. X                fprintf(stderr,
  385. X                    "%s: illegal argument to -b: %d\n",
  386. X                    progname, optarg);
  387. X                ++errflag;
  388. X            } else
  389. X                maxsize <<= 10;        /* multiple of 1k */
  390. X            break;
  391. X        case 'c':    /* specify alternate configuration file */
  392. X            cffile = optarg;
  393. X            break;
  394. X        case 'd':    /* debugging output */
  395. X            ++debug;
  396. X            break;
  397. X        case 'e':    /* copy executables */
  398. X            ++doexecs;
  399. X            break;
  400. X        case 'v':    /* be (more and more) verbose */
  401. X            ++verbose;
  402. X            break;
  403. X        case 'o':    /* specify alternate backup location */
  404. X            backup = optarg;
  405. X            break;
  406. X        case 'z':    /* fake it */
  407. X            ++dryrun;
  408. X            break;
  409. X        default:
  410. X            ++errflag;
  411. X        }
  412. X    }
  413. X    (void) time(&now);
  414. X    if ((cf_fp = fopen(cffile, "r+")) == NULL) {
  415. X        fprintf(stderr, "%s: open(%s): %s\n",
  416. X                progname, cffile, EMSG(errno) /* ... */);
  417. X        Usage();
  418. X    }
  419. X    if (verbose
  420. X        && flock(fileno(cf_fp), LOCK_EX|LOCK_NB) < 0
  421. X        && errno == EWOULDBLOCK)
  422. X        printf("waiting for exclusive lock on %s\n", cffile);
  423. X    if (flock(fileno(cf_fp), LOCK_EX) < 0) {
  424. X        fprintf(stderr, "%s: can't get lock on %s\n",
  425. X                progname, cffile);
  426. X        exit(1);
  427. X    } else if (verbose > 1) {
  428. X        setbuf(stdout, (char *)NULL);
  429. X        printf("Start at %slocked %s\n", ctime(&now), cffile);
  430. X    }
  431. X    cf = readconfig(cf_fp);
  432. X    if (optind == argc) {
  433. X        /* figure out what we should back up */
  434. X        for (cfp = cf; cfp != NULL; cfp = cfp->next) {
  435. X            if (cfp->lasttime == 0) {
  436. X                if (debug)
  437. X                    printf("%s: lasttime is 0\n", cfp->path);
  438. X                continue;
  439. X            }
  440. X            /* give 5 minutes leeway */
  441. X            if (cfp->lasttime + interval(cfp->interval) < now+300) {
  442. X                /* add this path to the list */
  443. X                if (debug)
  444. X                    printf("adding %s\n", cfp->path);
  445. X                addpath(cfp);
  446. X            } else if (debug)
  447. X            printf("ignoring %s: lasttime=%d interval=%s now=%d\n",
  448. X                cfp->path, cfp->lasttime, cfp->interval, now);
  449. X        }
  450. X    } else {
  451. X        for (; optind < argc; ++optind) {
  452. X            for (cfp = cf; cfp != NULL; cfp = cfp->next) {
  453. X                if (cfp->lasttime == 0)
  454. X                    continue;
  455. X                if (strcmp(cfp->path, argv[optind]) == 0) {
  456. X                    addpath(cfp);
  457. X                    break;
  458. X                }
  459. X            }
  460. X            if (cfp == NULL) {
  461. X                /* append new entry to config file */
  462. X                for (cfp = cf; cfp != NULL && cfp->next != NULL;
  463. X                     cfp = cfp->next)
  464. X                    continue;
  465. X                if (cfp != NULL) {
  466. X                    cfp->next = (struct config *)
  467. X                     emalloc((u_int)sizeof (struct config));
  468. X                    cfp = cfp->next;
  469. X                    cfp->next = NULL;
  470. X                    cfp->line = NULL;
  471. X                    cfp->interval = "24h";
  472. X                    cfp->path = argv[optind];
  473. X                    cfp->filter = "/dev/null";
  474. X                }
  475. X                cfp->lasttime = now - DELTA;
  476. X                addpath(cfp);
  477. X            }
  478. X        }
  479. X    }
  480. X
  481. X    if ((devbackup = getdevice(backup, MNTTAB)) == NULL &&
  482. X        (devbackup = getdevice(backup, MOUNTED)) == NULL) {
  483. X        fprintf(stderr, "%s: could not determine backup device\n",
  484. X                progname);
  485. X        exit(1);
  486. X    }
  487. X    if ((backupmnt = getpath(devbackup, MNTTAB)) == NULL &&
  488. X        (backupmnt = getpath(devbackup, MOUNTED)) == NULL) {
  489. X        fprintf(stderr, "%s: could not determine mount point for %s\n",
  490. X                progname, devbackup);
  491. X        ++errflag;
  492. X    }
  493. X    if (errflag)
  494. X        Usage();
  495. X    (void) umask(0);
  496. X    for (lp = devlist; lp != NULL; lp = lp->next)
  497. X        if (doit(lp->paths, lp->text))
  498. X            for (pp = lp->paths; pp != NULL; pp = pp->next)
  499. X                pp->config->lasttime = now;
  500. X    if (!dryrun)
  501. X        writeconfig(cf_fp, cf);
  502. X    (void) flock(fileno(cf_fp), LOCK_UN);
  503. X    if (verbose > 1) {
  504. X        time(&now);
  505. X        printf("unlocked %s\nEnd at %s", cffile, ctime(&now));
  506. X    }
  507. X    (void) fclose(cf_fp);
  508. X#ifdef PROF
  509. X    /* drop profiling data in a known place */
  510. X    chdir("/tmp");
  511. X#endif
  512. X    exit(0);
  513. X}
  514. X
  515. XUsage()
  516. X{
  517. X    fprintf(stderr,
  518. X"Usage: %s [ -devz -b# ] [ -c backup.conf ] [ -o /backup ] [ /path ... ]\n",
  519. X            progname);
  520. X    exit(1);
  521. X}
  522. X
  523. X/*
  524. X * The filter files contain glob expressions, one per line.  We need to
  525. X * keep track of which filter files we've read in, since several paths
  526. X * may share the same filters.
  527. X */
  528. X
  529. Xstruct filter {
  530. X    char    *name;
  531. X    char    *contents;
  532. X} filters[50];
  533. X
  534. Xint    maxfilters = 0;
  535. X
  536. X/* return pointer to contents of a filter file stored away */
  537. X
  538. Xchar *
  539. Xreadfilter(file)
  540. X    char    *file;
  541. X{
  542. X    int        i, fd;
  543. X    struct stat    stbuf;
  544. X
  545. X    if (strcmp(file, "/dev/null") == 0)
  546. X        return NULL;
  547. X    for (i = 0; i < maxfilters
  548. X           && i < (sizeof filters/sizeof (struct filter)); ++i) {
  549. X        if (strcmp(file, filters[i].name) == 0)
  550. X            return filters[i].contents;
  551. X    }
  552. X    if (i == (sizeof filters/sizeof (struct filter))) {
  553. X        fprintf(stderr, "%s: ran out of filters\n", progname);
  554. X        exit(1);
  555. X    }
  556. X    if ((fd = open(file, O_RDONLY, 0)) < 0) {
  557. X        fprintf(stderr, "%s: can't open filter file %s\n",
  558. X                progname, file);
  559. X        exit(1);
  560. X    }
  561. X    if (fstat(fd, &stbuf) < 0) {
  562. X        fprintf(stderr, "%s: can't stat open filter file %s\n",
  563. X                progname, file);
  564. X        exit(1);
  565. X    }
  566. X    filters[i].contents = emalloc((u_int)stbuf.st_size+1);
  567. X    if (read(fd, filters[i].contents, stbuf.st_size) < stbuf.st_size) {
  568. X        fprintf(stderr, "%s: couldn't read %d bytes from %s\n",
  569. X                progname, stbuf.st_size, file);
  570. X    }
  571. X    *(filters[i].contents+stbuf.st_size) = '\0';
  572. X    filters[i].name = file;
  573. X    ++maxfilters;
  574. X    (void) close(fd);
  575. X    return filters[i].contents;
  576. X}
  577. X
  578. X/*
  579. X * We maintain a two-level linked list structure (i.e. list of lists),
  580. X * associating paths with their raw device.  When there are several paths
  581. X * on the same device, we want to handle them simultaneously and only
  582. X * do the ilist walking once per device.  The root of this structure is
  583. X * devlist.
  584. X */
  585. X
  586. Xint
  587. Xaddpath(cfp)
  588. X    struct config    *cfp;
  589. X{
  590. X    struct a_dev    *lp;
  591. X    struct a_path    *pp;
  592. X    char    *rawdevice;
  593. X
  594. X    if (cfp->path == NULL || *cfp->path != '/') {
  595. X        fprintf(stderr, "%s: illegal path: %s\n",
  596. X                progname,
  597. X                cfp->path == NULL ? "(null)" : cfp->path);
  598. X    } else if ((rawdevice = getdevice(cfp->path, MNTTAB)) == NULL) {
  599. X        fprintf(stderr, "%s: no device for %s\n",
  600. X                progname, cfp->path);
  601. X    } else if (*rawdevice != '/') {
  602. X        fprintf(stderr, "%s: bad device %s for %s\n",
  603. X                progname, rawdevice, cfp->path);
  604. X    } else {
  605. X        /* link the path, device pair into lists */
  606. X        for (lp = devlist; lp != NULL; lp = lp->next)
  607. X            if (strcmp(lp->text, rawdevice) == 0)
  608. X                break;
  609. X        pp = (struct a_path *)
  610. X                emalloc((u_int)sizeof (struct a_path));
  611. X        pp->path = cfp->path;
  612. X        pp->newer = cfp->lasttime;
  613. X        pp->glob = readfilter(cfp->filter);
  614. X        pp->config = cfp;
  615. X        if (lp != NULL)
  616. X            pp->next = lp->paths;
  617. X        else {
  618. X            lp = (struct a_dev *)
  619. X                emalloc((u_int)sizeof (struct a_dev));
  620. X            lp->next = devlist;
  621. X            devlist = lp;
  622. X            lp->text = emalloc((u_int)strlen(rawdevice)+1);
  623. X            (void) strcpy(lp->text, rawdevice);
  624. X            pp->next = NULL;
  625. X        }
  626. X        lp->paths = pp;
  627. X    }
  628. X}
  629. X
  630. X/* return the name of the raw device corresponding to a particular file */
  631. X
  632. Xchar *
  633. Xgetdevice(path, table)
  634. X    char *path;
  635. X    char *table;
  636. X{
  637. X    char    *cp, *s;
  638. X    struct mntent *me;
  639. X    FILE    *fp;
  640. X    struct stat stpath, stdev;
  641. X
  642. X    if (lstat(path, &stpath) < 0) {
  643. X        fprintf(stderr, "%s: lstat(%s): %s\n",
  644. X                progname, path, EMSG(errno));
  645. X        return NULL;
  646. X    }
  647. X    if (!((stpath.st_mode & S_IFMT) & (S_IFREG | S_IFDIR))) {
  648. X        fprintf(stderr, "%s: %s is a special file\n", progname, path);
  649. X        return NULL;
  650. X    }
  651. X    if ((fp = setmntent(table, "r")) == NULL) {
  652. X        fprintf(stderr, "%s: setmntent(%s): %s\n",
  653. X                progname, table, EMSG(errno));
  654. X        return NULL;
  655. X    }
  656. X    while ((me = getmntent(fp)) != NULL) {
  657. X        if (strcmp(me->mnt_type, MNTTYPE_42) == 0
  658. X            && strncmp(me->mnt_fsname, "/dev/", 5) == 0
  659. X            && lstat(me->mnt_fsname, &stdev) == 0
  660. X            && stdev.st_rdev == stpath.st_dev) {
  661. X            (void) endmntent(fp);
  662. X            cp = emalloc((u_int)strlen(me->mnt_fsname)+2);
  663. X            (void) strcpy(cp, me->mnt_fsname);
  664. X            s = cp + strlen(cp) + 1;
  665. X            while (s > cp && *(s - 1) != '/')
  666. X                *s = *(s-1), --s;
  667. X            if (s > cp)
  668. X                *s = 'r';
  669. X            return cp;
  670. X        }
  671. X    }
  672. X    (void) endmntent(fp);
  673. X    return NULL;
  674. X}
  675. X
  676. X/* get the mount point of the filesystem on the raw device */
  677. X
  678. Xchar *
  679. Xgetpath(device, table)
  680. X    char *device;
  681. X    char *table;
  682. X{
  683. X    char        *cp;
  684. X    struct mntent    *me;
  685. X    FILE        *fp;
  686. X    char        devpath[MAXPATHLEN];
  687. X    struct stat    stpath, stdev;
  688. X    extern char    *rindex();
  689. X
  690. X    (void) strcpy(devpath, device);
  691. X    if ((cp = rindex(devpath, '/')) != NULL) {
  692. X        ++cp;
  693. X        if (*cp == 'r')
  694. X            while ((*cp = *(cp+1)) != '\0')
  695. X                ++cp;
  696. X    }
  697. X    if ((fp = setmntent(table, "r")) == NULL) {
  698. X        fprintf(stderr, "%s: setmntent(%s): %s\n",
  699. X                progname, table, EMSG(errno));
  700. X        return NULL;
  701. X    }
  702. X    while ((me = getmntent(fp)) != NULL) {
  703. X        if (strcmp(me->mnt_type, MNTTYPE_42) == 0
  704. X            && strcmp(me->mnt_fsname, devpath) == 0
  705. X            && lstat(me->mnt_fsname, &stdev) == 0
  706. X            && lstat(me->mnt_dir, &stpath) == 0
  707. X            && stdev.st_rdev == stpath.st_dev) {
  708. X            (void) endmntent(fp);
  709. X            cp = emalloc((u_int)strlen(me->mnt_dir)+1);
  710. X            (void) strcpy(cp, me->mnt_dir);
  711. X            return cp;
  712. X        }
  713. X    }
  714. X    (void) endmntent(fp);
  715. X    return NULL;
  716. X}
  717. X
  718. X#define sblock    sb_un.u_sblock
  719. X
  720. Xstruct iinfo {
  721. X    int    inum;            /* must be int so can be -ve too */
  722. X    u_int    blks;
  723. X    time_t    mtime;
  724. X};
  725. X
  726. Xint
  727. Xcmpiinfo(iip1, iip2)
  728. X    register struct iinfo *iip1, *iip2;
  729. X{
  730. X    return iip1->mtime - iip2->mtime;
  731. X}
  732. X
  733. Xstruct iinfo    *stack[2];
  734. Xlong        stacksize[2];
  735. Xlong        needspace[2];
  736. Xint        top = -1;
  737. X
  738. Xchar        *dirmask;
  739. Xint        mustfree;
  740. X
  741. X#define    SET(v,i)    ((v)[(i)/BITSPERBYTE] |= (1<<((i)%BITSPERBYTE)))
  742. X#define    TST(v,i)    ((v)[(i)/BITSPERBYTE] & (1<<((i)%BITSPERBYTE)))
  743. X
  744. Xint
  745. Xdoit(path, dev)
  746. X    struct a_path    *path;
  747. X    char        *dev;
  748. X{
  749. X    register struct iinfo    *st;
  750. X    register int    i, inum;
  751. X    int    fd, hiwater, lowater;
  752. X    u_int    nfiles;
  753. X    union { struct fs u_sblock; char dummy[SBSIZE]; } sb_un;
  754. X    char    *bitvec, *dirvec, pathbuf[MAXPATHLEN];
  755. X    struct a_path    *pp;
  756. X    struct statfs    fsbuf;
  757. X    extern int itest(), mkbackup(), rmbackup();
  758. X    extern char *calloc();
  759. X
  760. X    if (debug)
  761. X        printf("doing %s\n", dev);
  762. X    if ((fd = open(dev, O_RDONLY, 0)) < 0) {
  763. X        fprintf(stderr, "%s: open(%s): %s\n",
  764. X                progname, dev, EMSG(errno));
  765. X        return 0;
  766. X    }
  767. X    if (bread(fd, SBLOCK, (char *)&sblock, (long) SBSIZE) < 0) {
  768. X        fprintf(stderr, "%s: can't read superblock from %s: %s\n",
  769. X                progname, dev, EMSG(errno));
  770. X        return 0;
  771. X    }
  772. X    (void) close(fd);
  773. X    nfiles = sblock.fs_ipg * sblock.fs_ncg;
  774. X    stack[++top] = (struct iinfo *)calloc(nfiles, sizeof (struct iinfo));
  775. X    stacksize[top] = 0;
  776. X    needspace[top] = 0;
  777. X    dirvec = calloc((nfiles/BITSPERBYTE)+1, 1);
  778. X    dirmask = dirvec;
  779. X    if (top == 0) {
  780. X        /* figure out the oldest lasttime before i-list walk */
  781. X        lasttime = now;
  782. X        for (pp = path; pp != NULL; pp = pp->next)
  783. X            if (pp->newer < lasttime)
  784. X                lasttime = pp->newer;
  785. X    }
  786. X    if (debug)
  787. X        printf("%s: scan %d inodes\n", dev, nfiles);
  788. X    (void) ilw(dev, itest, 1);
  789. X    if (verbose)
  790. X        printf("%s: found %d candidate files, with %d blocks total\n",
  791. X                  dev, stacksize[top], needspace[top]);
  792. X    if (stacksize[top] == 0) {
  793. X        (void) free(dirvec);
  794. X        (void) free((char *)stack[top--]);
  795. X        return 0;
  796. X    }
  797. X    bitvec = calloc((nfiles/BITSPERBYTE)+1, 1);
  798. X    if (top == 0) {
  799. X        /*
  800. X         * This is the filesystem we want to back up.
  801. X         * First make sure there is enough free space in the backup
  802. X         * filesystem (if not, call myself recursively), then run
  803. X         * a file tree walker to copy the indicated files to the
  804. X         * backup filesystem.
  805. X         */
  806. X        if (statfs(backup, &fsbuf) < 0) {
  807. X            fprintf(stderr, "%s: statfs(%s): %s\n",
  808. X                    progname, backup, EMSG(errno));
  809. X            exit(1);
  810. X        }
  811. X        hiwater = (fsbuf.f_blocks-fsbuf.f_bfree
  812. X                        +fsbuf.f_bavail)*HIWATER/100;
  813. X        if (fsbuf.f_blocks - fsbuf.f_bfree + needspace[top] > hiwater) {
  814. X            /* need to free some space */
  815. X            struct a_path    backupdesc;
  816. X            /*
  817. X             * If you want to free so free space will be at
  818. X             * LOWATER after backup finishes, then enable the
  819. X             * next line and do s/hiwater/lowater/ in the
  820. X             * following line defining mustfree.
  821. X             */
  822. X            /* lowater = +(hiwater*LOWATER)/HIWATER; */
  823. X            mustfree = fsbuf.f_blocks - fsbuf.f_bfree
  824. X                          + needspace[top] - hiwater;
  825. X            /* select all files */
  826. X            lasttime = 0;
  827. X            maxsize = sblock.fs_dsize * DEV_BSIZE;
  828. X            backupdesc.path = backup;
  829. X            backupdesc.next = NULL;
  830. X            backupdesc.glob = NULL;
  831. X            backupdesc.newer = lasttime;
  832. X            if (!doit(&backupdesc, devbackup)) {
  833. X                fprintf(stderr,
  834. X                    "%s: Can't walk %s to free space\n",
  835. X                        progname, backup);
  836. X                exit(1);
  837. X            }
  838. X        }
  839. X        for (i = 0, st = stack[top]; i < nfiles; ++i, ++st) {
  840. X            if (st->mtime > 0) {
  841. X                SET(bitvec, st->inum);
  842. X            }
  843. X        }
  844. X        for (; path != NULL; path = path->next) {
  845. X            if (chdir(path->path) < 0) {
  846. X                fprintf(stderr, "%s: chdir(%s): %s\n",
  847. X                    progname, path->path, EMSG(errno));
  848. X                exit(1);
  849. X            }
  850. X            (void) sprintf(pathbuf, "%s/", path->path);
  851. X            lasttime = path->newer;
  852. X            if (verbose)
  853. X                printf("%s: select mtime within %d sec in %s\n",
  854. X                        dev, now - lasttime, path->path);
  855. X            walk(pathbuf, pathbuf + strlen(pathbuf), path->glob,
  856. X                      mkbackup, bitvec, dirvec);
  857. X        }
  858. X    } else {
  859. X        /*
  860. X         * This is the backup filesystem.
  861. X         * Sort the inodes selected into oldest-first, then run
  862. X         * a file tree walker to delete the files until we have
  863. X         * enough space *and* are under the low water mark.
  864. X         */
  865. X
  866. X        /* assert strcmp(path->path, backup) == 0 */
  867. X        /* compress the inode array */
  868. X        st = stack[top];
  869. X        for (i = inum = 0; i < stacksize[top]; ++inum) {
  870. X            if ((st+inum)->mtime > 0) {
  871. X                (st+i)->inum = inum;
  872. X                (st+i)->blks = (st+inum)->blks;
  873. X                (st+i)->mtime = (st+inum)->mtime;
  874. X                ++i;
  875. X            }
  876. X        }
  877. X        if (chdir(path->path) < 0) {
  878. X            fprintf(stderr, "%s: chdir(%s): %s\n",
  879. X                    progname, path->path, EMSG(errno));
  880. X            exit(1);
  881. X        }
  882. X        (void) sprintf(pathbuf, "%s/", path->path);
  883. X        if (strcmp(backup, backupmnt) != 0) {
  884. X            /* backup area is not an entire filesystem */
  885. X            walk(pathbuf, pathbuf + strlen(pathbuf), (char *)NULL,
  886. X                      (int (*)())NULL, (char *)NULL, dirvec);
  887. X            /* now all possible inums are negative and rest +ve */
  888. X            st = stack[top];
  889. X            for (i = inum = 0; i < stacksize[top]; ++inum) {
  890. X                if ((st+inum)->inum < 0) {
  891. X                    (st+i)->inum = inum;
  892. X                    ++i;
  893. X                } else
  894. X                    (st+i)->inum = 0;
  895. X            }
  896. X            /* now all possible inums are +ve and rest 0 */
  897. X        }
  898. X        /* sort it oldest first */
  899. X        qsort((char *)stack[top], stacksize[top],
  900. X              sizeof (struct iinfo), cmpiinfo);
  901. X        /* mustfree has been set in our parent doit() */
  902. X        /* go from oldest to newest, truncate after mustfree blocks */
  903. X        st = stack[top];
  904. X        for (i = 0; i < stacksize[top] && mustfree > 0; ++i, ++st) {
  905. X            if (st->inum > 2 && st->mtime > 0) {
  906. X                mustfree -= st->blks;
  907. X                SET(bitvec, st->inum);
  908. X            }
  909. X        }
  910. X        (void) sprintf(pathbuf, "%s/", path->path);
  911. X        walk(pathbuf, pathbuf + strlen(pathbuf), (char *)NULL,
  912. X                  rmbackup, bitvec, dirvec);
  913. X    }
  914. X    (void) free(bitvec);
  915. X    (void) free(dirvec);
  916. X    (void) free((char *)stack[top--]);
  917. X    return 1;
  918. X}
  919. X
  920. X/* This routine is used by the inode list walker) to test for relevant inodes */
  921. X
  922. Xitest(ip, inum)
  923. X    struct dinode    *ip;
  924. X    int        inum;
  925. X{
  926. X    register struct iinfo *iip;
  927. X
  928. X    if ((ip->di_mode & S_IFMT) == S_IFREG
  929. X        && ip->di_mtime > lasttime
  930. X        && ip->di_size < maxsize
  931. X        && (doexecs || (ip->di_mode & 07111) == 0)) {
  932. X        /* we have a candidate for backing up */
  933. X        iip = stack[top] + inum;
  934. X        iip->inum = inum;
  935. X        stacksize[top] += 1;
  936. X        needspace[top] += (iip->blks = ip->di_blocks);
  937. X        iip->mtime = ip->di_mtime;
  938. X        /*
  939. Xprintf("%6d:\tmode=0%o uid=%d gid=%d size=%d nlink=%d, a=%D m=%D c=%D\n",
  940. X        inum, ip->di_mode, ip->di_uid, ip->di_gid, ip->di_size,
  941. X        ip->di_nlink, ip->di_atime, ip->di_mtime, ip->di_ctime);
  942. X        */
  943. X    } else if ((ip->di_mode & S_IFMT) == S_IFDIR)
  944. X        SET(dirmask, inum);
  945. X    return 0;
  946. X}
  947. X
  948. X/*
  949. X * Create all the directories down a particular backup path, copying stat
  950. X * info from the original directories.
  951. X */
  952. X
  953. Xcreatdirs(dirpath, origpath, stbufp)
  954. X    char        *dirpath, *origpath;
  955. X    struct stat    *stbufp;
  956. X{
  957. X    char *cp;
  958. X    struct stat    stbuf;
  959. X    extern char *rindex();
  960. X
  961. X    if (mkdir(dirpath, stbufp->st_mode & 0777) < 0) {
  962. X        if (errno == ENOENT) {
  963. X            if ((cp = rindex(dirpath, '/')) > dirpath) {
  964. X                *cp = '\0';
  965. X                creatdirs(dirpath, origpath, stbufp);
  966. X                *cp = '/';
  967. X            }
  968. X            (void) mkdir(dirpath, (stbufp->st_mode & 0777)|0111);
  969. X            if (stat(origpath, &stbuf) == 0) {
  970. X                (void) chown(dirpath, stbuf.st_uid,
  971. X                              stbuf.st_gid);
  972. X                if (stbuf.st_mode & 0400)
  973. X                    stbuf.st_mode |= 0100;
  974. X                if (stbuf.st_mode & 040)
  975. X                    stbuf.st_mode |= 010;
  976. X                if (stbuf.st_mode & 04)
  977. X                    stbuf.st_mode |= 01;
  978. X                (void) chmod(dirpath, stbuf.st_mode & 0777);
  979. X            } else
  980. X                fprintf(stderr, "%s: stat(%s): %s\n",
  981. X                    progname, origpath, EMSG(errno));
  982. X        }
  983. X    } else if (stat(origpath, &stbuf) == 0) {
  984. X        (void) chown(dirpath, stbuf.st_uid, stbuf.st_gid);
  985. X        if (stbuf.st_mode & 0400)
  986. X            stbuf.st_mode |= 0100;
  987. X        if (stbuf.st_mode & 040)
  988. X            stbuf.st_mode |= 010;
  989. X        if (stbuf.st_mode & 04)
  990. X            stbuf.st_mode |= 01;
  991. X        (void) chmod(dirpath, stbuf.st_mode & 0777);
  992. X    } else
  993. X        fprintf(stderr, "%s: stat(%s): %s\n",
  994. X                progname, origpath, EMSG(errno));
  995. X}
  996. X
  997. X/* Create an actual backup file, return its file descriptor */
  998. X
  999. Xint
  1000. Xcreatbackup(path, stbufp, filename)
  1001. X    char    *path, *filename;
  1002. X    struct stat    *stbufp;
  1003. X{
  1004. X    int    fd;
  1005. X    char    *cp, *ct;
  1006. X
  1007. X    ct = ctime(&stbufp->st_mtime);
  1008. X    (void) sprintf(filename, "%s%s/", backup, path);
  1009. X    cp = filename + strlen(filename);
  1010. X    *cp++ = ct[4]; *cp++ = ct[5]; *cp++ = ct[6];
  1011. X    *cp++ = ct[8] == ' ' ? '0' : ct[8]; *cp++ = ct[9];
  1012. X    *cp++ = '-';
  1013. X    *cp++ = ct[11]; *cp++ = ct[12]; *cp++ = ct[13];
  1014. X    *cp++ = ct[14]; *cp++ = ct[15]; *cp = '\0';
  1015. X    fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, stbufp->st_mode & 0777);
  1016. X    if (fd < 0) {
  1017. X        if (errno == ENOENT) {
  1018. X            cp = rindex(filename, '/');
  1019. X            *cp = '\0';
  1020. X            creatdirs(filename, filename+strlen(backup), stbufp);
  1021. X            *cp = '/';
  1022. X        }
  1023. X        fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC,
  1024. X                    stbufp->st_mode & 0777);
  1025. X    }
  1026. X    if (fd < 0) {
  1027. X        fprintf(stderr, "%s: open(%s): %s\n",
  1028. X                progname, filename, EMSG(errno));
  1029. X        return -1;
  1030. X    }
  1031. X    (void) fchown(fd, stbufp->st_uid, stbufp->st_gid);
  1032. X    return fd;
  1033. X}
  1034. X
  1035. X/* This routine called from walk() to make a backup of a file */
  1036. X
  1037. Xint
  1038. Xmkbackup(path, glob)
  1039. X    char    *path, *glob;
  1040. X{
  1041. X    struct stat    stbuf;
  1042. X    int    n, bfd, fd;
  1043. X    char    bigbuf[8*1024], bpath[MAXPATHLEN];
  1044. X    struct timeval tv[2];
  1045. X
  1046. X    if (doglob(glob, path))
  1047. X        return;
  1048. X    if ((fd = open(path, O_RDONLY, 0)) < 0) {
  1049. X        /*
  1050. X         * File may have been removed under our feet.
  1051. X         * Don't make noise about that.
  1052. X         */
  1053. X        if (errno == ENOENT)
  1054. X            return;
  1055. X        fprintf(stderr, "%s: open(%s): %s\n",
  1056. X                progname, path, EMSG(errno));
  1057. X        return;
  1058. X    }
  1059. X    if (fstat(fd, &stbuf) < 0) {
  1060. X        fprintf(stderr, "%s: fstat(%s): %s\n",
  1061. X                progname, path, EMSG(errno));
  1062. X        (void) close(fd);
  1063. X        return;
  1064. X    }
  1065. X    if (stbuf.st_mtime < lasttime) {
  1066. X        (void) close(fd);
  1067. X        return;
  1068. X    }
  1069. X    if (stbuf.st_size == 0) {
  1070. X        (void) close(fd);
  1071. X        return;
  1072. X    }
  1073. X    if (dryrun || verbose > 1)
  1074. X        printf("copy %s\n", path);
  1075. X    if (dryrun) {
  1076. X        (void) close(fd);
  1077. X        return;
  1078. X    }
  1079. X    if ((bfd = creatbackup(path, &stbuf, bpath)) < 0) {
  1080. X        (void) close(fd);
  1081. X        return;
  1082. X    }
  1083. X    while ((n = read(fd, bigbuf, sizeof bigbuf)) > 0)
  1084. X        if (write(bfd, bigbuf, n) < n) {
  1085. X            fprintf(stderr, "%s: write error: %s\n",
  1086. X                    progname, EMSG(errno));
  1087. X            /* saving a little bit is better than nothing... */
  1088. X            break;
  1089. X        }
  1090. X    (void) close(fd);
  1091. X    (void) close(bfd);
  1092. X    /* Preserve file times, so "find /backup -newer ..." works */
  1093. X    tv[0].tv_sec = stbuf.st_atime;
  1094. X    tv[1].tv_sec = stbuf.st_mtime;
  1095. X    tv[0].tv_usec = tv[1].tv_usec = 0;
  1096. X    (void) utimes(bpath, tv);
  1097. X}
  1098. X
  1099. Xstatic int rmcnt;
  1100. X
  1101. X/* This routine is called from walk() to rm a backup file (and parent dir) */
  1102. X
  1103. X/* ARGSUSED */
  1104. Xint
  1105. Xrmbackup(path, glob)
  1106. X    char    *path, *glob;
  1107. X{
  1108. X
  1109. X    if (dryrun || verbose > 1)
  1110. X        printf("remove %s\n", path);
  1111. X    if (dryrun)
  1112. X        return;
  1113. X    (void) unlink(path);
  1114. X    ++rmcnt;
  1115. X}
  1116. X
  1117. X/* a file tree walker (a la ftw(3)) for this application (see ftw(3) BUGS) */
  1118. X
  1119. Xwalk(path, cp, glob, fn, vector, dirvec)
  1120. X    char    *path, *cp, *glob;
  1121. X    int    (*fn)();
  1122. X    char    *vector, *dirvec;
  1123. X{
  1124. X    register struct direct *dp;
  1125. X    register DIR *dirp;
  1126. X    register char *eos;
  1127. X    register int n;
  1128. X
  1129. X    if ((dirp = opendir(".")) == NULL) {
  1130. X        fprintf(stderr, "%s: opendir(%s): %s\n",
  1131. X                progname, path, EMSG(errno));
  1132. X        /* error is usually "too many open files", so don't exit */
  1133. X        return;
  1134. X    }
  1135. X    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
  1136. X        if (dp->d_name[0] == '.'
  1137. X            && (dp->d_namlen == 1
  1138. X            || (dp->d_name[1] == '.' && dp->d_namlen == 2)))
  1139. X            continue;
  1140. X        if (vector == NULL)    /* magic for backup partition */
  1141. X            (stack[top]+dp->d_fileno)->inum = -dp->d_fileno;
  1142. X        if (vector != NULL && TST(vector, dp->d_fileno)) {
  1143. X            (void) strcpy(cp, dp->d_name);
  1144. X            (*fn)(path, glob);
  1145. X        } else if (TST(dirvec, dp->d_fileno)
  1146. X                  && chdir(dp->d_name) == 0) {
  1147. X            (void) strcpy(cp, dp->d_name);
  1148. X            eos = cp + dp->d_namlen;
  1149. X            *eos++ = '/';
  1150. X            *eos = '\0';
  1151. X            n = rmcnt;
  1152. X            walk(path, eos, glob, fn, vector, dirvec);
  1153. X            (void) chdir("..");
  1154. X            if (fn == rmbackup && n != rmcnt) {
  1155. X                *--eos = '\0';    /* clobber trailing '/' */
  1156. X                (void) rmdir(path);
  1157. X            }
  1158. X        }
  1159. X    }
  1160. X    (void) closedir(dirp);
  1161. X}
  1162. X
  1163. X/* Malloc that prints error and dies if it can't honour memory request */
  1164. X
  1165. Xchar *
  1166. Xemalloc(n)
  1167. X    u_int    n;
  1168. X{
  1169. X    char *cp;
  1170. X    extern char *malloc();
  1171. X
  1172. X    if ((cp = malloc(n)) == NULL) {
  1173. X        fprintf(stderr, "%s: malloc failure!\n", progname);
  1174. X        exit(1);
  1175. X    }
  1176. X    return cp;
  1177. X}
  1178. X
  1179. X/* Read and parse the configuration file */
  1180. X
  1181. Xstruct config *
  1182. Xreadconfig(fp)
  1183. X    FILE    *fp;
  1184. X{
  1185. X    struct config    *cfe, *cfhead, **pcfenp;
  1186. X    char    *cp, *s, buf[BUFSIZ];
  1187. X
  1188. X    cfhead = NULL;
  1189. X    pcfenp = &cfhead;
  1190. X    rewind(fp);
  1191. X    while (fgets(buf, sizeof buf, fp) != NULL) {
  1192. X        cfe = (struct config *)emalloc((u_int)sizeof (struct config));
  1193. X        cfe->line = emalloc((u_int)strlen(buf));
  1194. X        (void) strncpy(cfe->line, buf, strlen(buf)-1);
  1195. X        cfe->next = NULL;
  1196. X        cp = buf;
  1197. X        while (isascii(*cp) && isspace(*cp))
  1198. X            ++cp;
  1199. X        s = cp;
  1200. X        while (isascii(*cp) && !isspace(*cp))
  1201. X            ++cp;
  1202. X        if (*s != '#')
  1203. X            *cp++ = '\0';
  1204. X        cfe->path = emalloc((u_int)strlen(s)+1);
  1205. X        (void) strcpy(cfe->path, s);
  1206. X        if (*s == '#') {
  1207. X            *(cfe->path+strlen(s)-1) = '\0';    /* kill NL */
  1208. X            cfe->lasttime = 0;
  1209. X            *pcfenp = cfe;
  1210. X            pcfenp = &cfe->next;
  1211. X            continue;
  1212. X        }
  1213. X        while (isascii(*cp) && isspace(*cp))
  1214. X            ++cp;
  1215. X        s = cp;
  1216. X        while (isascii(*cp) && !isspace(*cp))
  1217. X            ++cp;
  1218. X        *cp++ = '\0';
  1219. X        cfe->interval = emalloc((u_int)strlen(s)+1);
  1220. X        (void) strcpy(cfe->interval, s);
  1221. X        while (isascii(*cp) && isspace(*cp))
  1222. X            ++cp;
  1223. X        s = cp;
  1224. X        while (isascii(*cp) && !isspace(*cp))
  1225. X            ++cp;
  1226. X        *cp++ = '\0';
  1227. X        cfe->filter = emalloc((u_int)strlen(s)+1);
  1228. X        (void) strcpy(cfe->filter, s);
  1229. X        while (isascii(*cp) && isspace(*cp))
  1230. X            ++cp;
  1231. X        /* interpret ctime */
  1232. X        *(cfe->line + (cp - buf)) = '\0';
  1233. X        cfe->lasttime = ctime2time(cp);
  1234. X        *pcfenp = cfe;
  1235. X        pcfenp = &cfe->next;
  1236. X    }
  1237. X    return cfhead;
  1238. X}
  1239. X
  1240. X/* Write the configuration file out with the updated information */
  1241. X
  1242. Xvoid
  1243. Xwriteconfig(fp, cfp)
  1244. X    FILE        *fp;
  1245. X    struct config    *cfp;
  1246. X{
  1247. X    rewind(fp);
  1248. X    for (; cfp != NULL; cfp = cfp->next) {
  1249. X        if (cfp->lasttime == 0) {
  1250. X            fprintf(fp, "%s\n", cfp->path);    /* comment */
  1251. X            continue;
  1252. X        }
  1253. X        if (cfp->line == NULL)    /* new entry */
  1254. X            fprintf(fp, "%s\t%s\t%s\t%s",
  1255. X                    cfp->path, cfp->interval, cfp->filter,
  1256. X                    ctime(&cfp->lasttime));
  1257. X        else
  1258. X            fprintf(fp, "%s%s", cfp->line, ctime(&cfp->lasttime));
  1259. X    }
  1260. X}
  1261. X
  1262. X/* Parse a ctime() string into seconds since epoch */
  1263. X
  1264. Xchar    *ap[] = {    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1265. X            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0 };
  1266. X
  1267. Xstruct timezone    tz = { 0 };
  1268. X
  1269. Xint
  1270. Xctime2time(s)
  1271. X    char    *s;
  1272. X{
  1273. X    static int    isdst, flag = 0;
  1274. X    int    sec, century, year, month, dayinmonth, julian, i;
  1275. X    struct timeval    tv;
  1276. X    struct tm *tms;
  1277. X
  1278. X    if (strlen(s) < 25)
  1279. X        return 0;
  1280. X    if (!flag) {
  1281. X        (void) gettimeofday(&tv, &tz);
  1282. X        tms = localtime(&now);
  1283. X        isdst = tms->tm_isdst;
  1284. X        flag = 1;
  1285. X    }
  1286. X    century = atoi(s+20)/100;
  1287. X    year = atoi(s+22);
  1288. X    dayinmonth = atoi(s+8);
  1289. X    for (i = 0; ap[i] != NULL; ++i) {
  1290. X        if (strncmp(s+4, ap[i], 3) == 0)
  1291. X            break;
  1292. X    }
  1293. X    if (ap[i] == NULL)
  1294. X        month = -1;
  1295. X    else {
  1296. X        if ((month = ++i) > 2)
  1297. X            month -= 3;
  1298. X        else
  1299. X            month += 9, year--;
  1300. X    }
  1301. X    sec = atoi(s+17) + 60*(atoi(s+14) + 60*atoi(s+11));
  1302. X    /* this is a standard julian date formula of unknown origin */
  1303. X    julian = (146097L * century)/4L + (1461L * year)/4L
  1304. X        + (153L * month + 2L)/5L + dayinmonth - 719469L;
  1305. X    sec += julian * 24 * 60 * 60 + (tz.tz_minuteswest-(isdst*60))*60;
  1306. X    return sec;
  1307. X}
  1308. X
  1309. X/* Parse an interval string, e.g. 2h30m or 8h or 15s, the obvious meanings */
  1310. X
  1311. Xint
  1312. Xinterval(s)
  1313. X    char    *s;
  1314. X{
  1315. X    int    i, sec;
  1316. X
  1317. X    if (s == NULL)
  1318. X        return 0;
  1319. X    i = sec = 0;
  1320. X    while (*s != '\0' && isascii(*s)) {
  1321. X        if (isdigit(*s)) {
  1322. X            i *= 10;
  1323. X            i += *s - '0';
  1324. X        } else if (*s == 'h')
  1325. X            sec += 3600*i, i = 0;
  1326. X        else if (*s == 'm')
  1327. X            sec += 60*i, i = 0;
  1328. X        else if (*s == 's')
  1329. X            sec += i, i = 0;
  1330. X        ++s;
  1331. X    }
  1332. X    return sec;
  1333. X}
  1334. X
  1335. X/* Test if path is de-selected by the glob patterns in the filter string */
  1336. X
  1337. Xint
  1338. Xdoglob(filter, path)
  1339. X    char    *filter, *path;
  1340. X{
  1341. X    register char    *cp;
  1342. X
  1343. X    for (cp = filter; cp != NULL && *cp != '\0';) {
  1344. X        if (match(cp, path))
  1345. X            return 1;
  1346. X        while (*cp != '\n' && *cp != '\0')
  1347. X            ++cp;
  1348. X        if (*cp == '\n')
  1349. X            ++cp;
  1350. X    }
  1351. X    return 0;
  1352. X}
  1353. X
  1354. X/* General glob pattern match routine, customized to exit on newline */
  1355. X
  1356. Xint
  1357. Xmatch(pattern, string)
  1358. X    register char    *pattern, *string;
  1359. X{
  1360. X    while (1)
  1361. X        switch (*pattern) {
  1362. X        case '*':
  1363. X            ++pattern;
  1364. X            do {
  1365. X                if (match(pattern, string))
  1366. X                    return 1;
  1367. X            } while (*string++ != '\0');
  1368. X            return 0;
  1369. X            break;
  1370. X        case '[':
  1371. X            if (*string == '\0')
  1372. X                return 0;
  1373. X            while ((*++pattern != ']') && (*pattern != *string))
  1374. X                if (*pattern == '\0')
  1375. X                    return 0;
  1376. X            if (*pattern == ']')
  1377. X                return 0;
  1378. X            while (*pattern++ != ']')
  1379. X                if (*pattern == '\0')
  1380. X                    return 0;
  1381. X            ++string;
  1382. X            break;
  1383. X        case '?':
  1384. X            ++pattern;
  1385. X            if (*string++ == '\0')
  1386. X                return 0;
  1387. X            break;
  1388. X        case '\n':
  1389. X            return (*string == '\0');
  1390. X        default:
  1391. X            if (*pattern++ != *string++)
  1392. X                return 0;
  1393. X        }
  1394. X}
  1395. END_OF_backup.c
  1396. if test 28044 -ne `wc -c <backup.c`; then
  1397.     echo shar: \"backup.c\" unpacked with wrong size!
  1398. fi
  1399. # end of overwriting check
  1400. fi
  1401. if test -f backup.conf -a "${1}" != "-c" ; then 
  1402.   echo shar: Will not over-write existing file \"backup.conf\"
  1403. else
  1404. echo shar: Extracting \"backup.conf\" \(363 characters\)
  1405. sed "s/^X//" >backup.conf <<'END_OF_backup.conf'
  1406. X# path            intvl    filter file        last done
  1407. X/homes/neat/car        8h    /ai/etc/backup.filter    Tue May 23 12:37:01 1989
  1408. X/homes/neat/cdr        8h    /ai/etc/backup.filter    Tue May 23 12:37:01 1989
  1409. X/homes/neat/lambda    8h    /ai/etc/backup.filter    Tue May 23 12:37:01 1989
  1410. X/homes/neat/eq        8h    /ai/etc/backup.filter    Tue May 23 12:37:01 1989
  1411. X/sys        8h    /ai/etc/backup.filter    Tue May 23 12:37:01 1989
  1412. END_OF_backup.conf
  1413. if test 363 -ne `wc -c <backup.conf`; then
  1414.     echo shar: \"backup.conf\" unpacked with wrong size!
  1415. fi
  1416. # end of overwriting check
  1417. fi
  1418. if test -f backup.filter -a "${1}" != "-c" ; then 
  1419.   echo shar: Will not over-write existing file \"backup.filter\"
  1420. else
  1421. echo shar: Extracting \"backup.filter\" \(109 characters\)
  1422. sed "s/^X//" >backup.filter <<'END_OF_backup.filter'
  1423. X*/core
  1424. X*/a.out
  1425. X*/*.o
  1426. X*/bin*
  1427. X*/#*
  1428. X*/*~
  1429. X*/.msg?rc
  1430. X*/.rnlast
  1431. X*/.rnsoft
  1432. X*/.oldnewsrc
  1433. X*/mbox
  1434. X*/.timecheck
  1435. X*/*.dvi
  1436. END_OF_backup.filter
  1437. if test 109 -ne `wc -c <backup.filter`; then
  1438.     echo shar: \"backup.filter\" unpacked with wrong size!
  1439. fi
  1440. # end of overwriting check
  1441. fi
  1442. if test -f getback.1 -a "${1}" != "-c" ; then 
  1443.   echo shar: Will not over-write existing file \"getback.1\"
  1444. else
  1445. echo shar: Extracting \"getback.1\" \(806 characters\)
  1446. sed "s/^X//" >getback.1 <<'END_OF_getback.1'
  1447. X.TH GETBACK 1 "UofToronto"
  1448. X.SH NAME
  1449. Xgetback \- Recover a backed up version of a file.
  1450. X.SH SYNOPSIS
  1451. X.B getback
  1452. X[ -o
  1453. X.I /backup
  1454. X]
  1455. X.I filename
  1456. X.SH DESCRIPTION
  1457. X.I Getback
  1458. Xfinds the the copies made of a given filename made by the backup(8)
  1459. Xprogram and allows them to be restored.
  1460. XThe default backup tree can be overridden with the command line option.
  1461. X.PP
  1462. XOnce the backup copies are found, all of them are printed in a list and
  1463. Xyou are asked which one you would like to restore.  If a file with the same
  1464. X.I filename
  1465. Xalready exists, you are asked to confirm before
  1466. X.I getback
  1467. Xwill overwrite it.
  1468. X.SH FILES
  1469. X.ta 2i
  1470. X/backup    - root of the backup filesystem
  1471. X.SH "SEE ALSO"
  1472. X.IR backup (8)
  1473. X.SH "INFO"
  1474. XBased on the original program by Larry Philps, this is a reimplementation
  1475. Xby Rayan Zachariassen, both at U of Toronto.
  1476. END_OF_getback.1
  1477. if test 806 -ne `wc -c <getback.1`; then
  1478.     echo shar: \"getback.1\" unpacked with wrong size!
  1479. fi
  1480. # end of overwriting check
  1481. fi
  1482. if test -f getback.c -a "${1}" != "-c" ; then 
  1483.   echo shar: Will not over-write existing file \"getback.c\"
  1484. else
  1485. echo shar: Extracting \"getback.c\" \(5861 characters\)
  1486. sed "s/^X//" >getback.c <<'END_OF_getback.c'
  1487. X/*
  1488. X * Getback - retrieve files stored away by the incremental backup mechanism.
  1489. X *
  1490. X * Based on an original program by Larry Philps.  This reimplementation is
  1491. X * Copyright 1988 by Rayan Zachariassen, solely to prevent you from selling
  1492. X * it or putting your name on it.  Free (gratis) redistribution is encouraged.
  1493. X */
  1494. X
  1495. X#include <stdio.h>
  1496. X#include <ctype.h>
  1497. X#include <signal.h>
  1498. X#include <sys/param.h>
  1499. X#include <sys/types.h>
  1500. X#include <sys/file.h>
  1501. X#include <sys/stat.h>
  1502. X#include <sys/dir.h>
  1503. X
  1504. X#define    BACKUP        "/backup"    /* root of backup hierarchy */
  1505. X
  1506. Xstruct copy {
  1507. X    char        *file;
  1508. X    struct stat    stbuf;
  1509. X    struct copy    *next;
  1510. X};
  1511. X
  1512. X#define    EMSG(x)    (errno < sys_nerr ? sys_errlist[x] : \
  1513. X           (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
  1514. X
  1515. Xextern int    errno, sys_nerr;
  1516. Xextern char    *sys_errlist[];
  1517. Xchar        errmsgbuf[30];
  1518. X
  1519. Xchar        *progname;
  1520. X
  1521. Xextern int    optind;
  1522. Xextern char    *optarg;
  1523. X
  1524. Xmain(argc, argv)
  1525. X    int    argc;
  1526. X    char    *argv[];
  1527. X{
  1528. X    int        c, errflag, i, copyindex, copyit, ifd, ofd, myuid;
  1529. X    char        *backup, *buf, line[BUFSIZ];
  1530. X    DIR        *dirp;
  1531. X    struct direct    *dp;
  1532. X    struct copy    *cep, *copyarr[1000];
  1533. X    struct stat    stbuf;
  1534. X    char        path[MAXPATHLEN], filename[MAXPATHLEN];
  1535. X    extern int    cmpcopy();
  1536. X    extern int    errno;
  1537. X    extern char    *emalloc(), *sbrk();
  1538. X
  1539. X    progname = argv[0];
  1540. X    backup = BACKUP;
  1541. X    errflag = 0;
  1542. X    while ((c = getopt(argc, argv, "o:")) != EOF) {
  1543. X        switch (c) {
  1544. X        case 'o':
  1545. X            backup = optarg;
  1546. X            break;
  1547. X        default:
  1548. X            ++errflag;
  1549. X        }
  1550. X    }
  1551. X    if (optind != argc - 1) {
  1552. X        fprintf(stderr, "%s: missing filename\n", progname);
  1553. X        ++errflag;
  1554. X    }
  1555. X    if (errflag) {
  1556. X        fprintf(stderr, "Usage: %s [ -o /backup ] filename\n",
  1557. X                progname);
  1558. X        exit(1);
  1559. X    }
  1560. X    if (argv[optind][0] == '/')
  1561. X        (void) strcpy(filename, argv[optind]);
  1562. X    else if (getwd(filename) == NULL) {
  1563. X        fprintf(stderr, "%s: %s\n", progname, filename);
  1564. X        exit(1);
  1565. X    } else
  1566. X        (void) sprintf(filename+strlen(filename), "/%s", argv[optind]);
  1567. X    (void) sprintf(path, "%s/%s", backup, filename);
  1568. X    if ((dirp = opendir(path)) == NULL) {
  1569. X        printf("There are no online backups of %s\n", argv[optind]);
  1570. X        exit(1);
  1571. X    }
  1572. X    copyindex = 0;
  1573. X    myuid = getuid();
  1574. X    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
  1575. X        if (dp->d_namlen == sizeof "Jan01-00:00" - 1
  1576. X            && dp->d_name[5] == '-' && dp->d_name[8] == ':') {
  1577. X            (void) sprintf(path, "%s/%s/%s", backup, filename, dp->d_name);
  1578. X            if (stat(path, &stbuf) < 0)
  1579. X                continue;
  1580. X            if (myuid != 0 && myuid != stbuf.st_uid)
  1581. X                continue;
  1582. X            cep = (struct copy *)emalloc((u_int)sizeof (struct copy));
  1583. X            cep->file = emalloc(strlen(path)+1);
  1584. X            (void) strcpy(cep->file, path);
  1585. X            cep->stbuf = stbuf;
  1586. X            copyarr[copyindex++] = cep;
  1587. X        }
  1588. X    }
  1589. X    (void) closedir(dirp);
  1590. X    if (copyindex == 0) {
  1591. X        printf("There are no online backups of \"%s\"\n", argv[optind]);
  1592. X        exit(1);
  1593. X    } else if (copyindex == 1) {
  1594. X        printf("There is only one backup of \"%s\", dated %s",
  1595. X                  argv[optind], ctime(©arr[0]->stbuf.st_mtime));
  1596. X        printf("Retrieve this copy [y] ? ");
  1597. X        fflush(stdout);
  1598. X        (void) gets(line);
  1599. X        if (line[0] == '\0' || line[0] == 'y' || line[0] == 'Y') {
  1600. X            copyit = 0;
  1601. X        } else {
  1602. X            fprintf(stderr, "%s: nothing changed\n", progname);
  1603. X            exit(1);
  1604. X        }
  1605. X    } else {
  1606. X        printf("There are %d backup versions of \"%s\" from:\n\n",
  1607. X                  copyindex, argv[optind]);
  1608. X        qsort((char *)copyarr, copyindex, sizeof copyarr[0], cmpcopy);
  1609. X        for (i = 0; i < copyindex; ++i) {
  1610. X            printf("\t%2d.\t%.24s\t(%ld bytes)\n",
  1611. X                   i+1, ctime(©arr[i]->stbuf.st_mtime),
  1612. X                   copyarr[i]->stbuf.st_size);
  1613. X        }
  1614. X        (void) putchar('\n');
  1615. X    retry:
  1616. X        printf("Enter number corresponding to version you want [%d]: ",
  1617. X                copyindex);
  1618. X        fflush(stdout);
  1619. X        if (gets(line) == NULL)
  1620. X            line[0] = 'n';
  1621. X        if (line[0] == '\0')
  1622. X            copyit = copyindex - 1;
  1623. X        else if (line[0] == 'n' || line[0] == 'N') {
  1624. X            fprintf(stderr, "%s: nothing changed\n", progname);
  1625. X            exit(1);
  1626. X        } else if (!isdigit(line[0])
  1627. X                 || (i = atoi(line)) <= 0 || i > copyindex) {
  1628. X            printf("Answer must be a number from 1-%d, or <cr>.\n",
  1629. X                       copyindex);
  1630. X            goto retry;
  1631. X        } else
  1632. X            copyit = i-1;
  1633. X    }
  1634. X    if (stat(argv[optind], &stbuf) == 0) {
  1635. X        if (stbuf.st_uid != myuid) {
  1636. X            fprintf(stderr, "%s: you are not owner of %s\n",
  1637. X                    progname, argv[optind]);
  1638. X            exit(1);
  1639. X        }
  1640. X        printf("\"%s\" exists, overwrite [y] ? ", argv[optind]);
  1641. X        (void) gets(line);
  1642. X        if (!(line[0] == '\0' || line[0] == 'y' || line[0] == 'Y')) {
  1643. X            fprintf(stderr, "%s: nothing changed\n", progname);
  1644. X            exit(1);
  1645. X        }
  1646. X    }
  1647. X    printf("Retrieving %.24s version of \"%s\" ... ",
  1648. X               ctime(©arr[copyit]->stbuf.st_mtime),
  1649. X               argv[optind]);
  1650. X    if ((buf = sbrk(copyarr[copyit]->stbuf.st_blksize)) == NULL) {
  1651. X        fprintf(stderr, "%s: sbrk(%d): %s\n",
  1652. X                progname, copyarr[copyit]->stbuf.st_blksize,
  1653. X                EMSG(errno));
  1654. X        exit(1);
  1655. X    }
  1656. X    if ((ifd = open(copyarr[copyit]->file, O_RDONLY, 0)) < 0) {
  1657. X        fprintf(stderr, "%s: open(%s): %s\n",
  1658. X                progname, copyarr[copyit]->file, EMSG(errno));
  1659. X        exit(1);
  1660. X    }
  1661. X
  1662. X    (void) sigsetmask(sigmask(SIGHUP)
  1663. X            | sigmask(SIGINT)
  1664. X            | sigmask(SIGQUIT)
  1665. X            | sigmask(SIGTERM)
  1666. X            | sigmask(SIGTSTP));
  1667. X
  1668. X    if ((ofd = open(argv[optind], O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
  1669. X        fprintf(stderr, "%s: open(%s): %s\n",
  1670. X                progname, argv[optind], EMSG(errno));
  1671. X        exit(1);
  1672. X    }
  1673. X    (void) umask(0);
  1674. X    (void) fchmod(ofd, copyarr[copyit]->stbuf.st_mode);
  1675. X    (void) fchown(ofd, copyarr[copyit]->stbuf.st_uid,
  1676. X               copyarr[copyit]->stbuf.st_gid);
  1677. X    while ((i = read(ifd, buf, copyarr[copyit]->stbuf.st_blksize)) > 0)
  1678. X        if (write(ofd, buf, i) < i) {
  1679. X            fprintf(stderr, "%s: write of %d bytes: %s\n",
  1680. X                    progname, i, EMSG(errno));
  1681. X            exit(1);
  1682. X        }
  1683. X    (void) close(ofd);
  1684. X    (void) close(ifd);
  1685. X    printf("done!\n");
  1686. X    exit(0);
  1687. X}
  1688. X
  1689. Xint
  1690. Xcmpcopy(a, b)
  1691. X    struct copy **a, **b;
  1692. X{
  1693. X    return (*a)->stbuf.st_mtime - (*b)->stbuf.st_mtime;
  1694. X}
  1695. X
  1696. Xchar *
  1697. Xemalloc(n)
  1698. X    u_int    n;
  1699. X{
  1700. X    char *cp;
  1701. X    extern char *malloc();
  1702. X
  1703. X    if ((cp = malloc(n)) == NULL) {
  1704. X        fprintf(stderr, "%s: malloc(%u) failed!\n", progname, n);
  1705. X        exit(1);
  1706. X    }
  1707. X    return cp;
  1708. X}
  1709. END_OF_getback.c
  1710. if test 5861 -ne `wc -c <getback.c`; then
  1711.     echo shar: \"getback.c\" unpacked with wrong size!
  1712. fi
  1713. # end of overwriting check
  1714. fi
  1715. if test -f getback.sh -a "${1}" != "-c" ; then 
  1716.   echo shar: Will not over-write existing file \"getback.sh\"
  1717. else
  1718. echo shar: Extracting \"getback.sh\" \(2205 characters\)
  1719. sed "s/^X//" >getback.sh <<'END_OF_getback.sh'
  1720. X#!/bin/sh
  1721. X# getback [-o /backup] filename
  1722. X
  1723. XPATH=/bin:/usr/bin:/usr/ucb; export PATH
  1724. XBACKUP=/backup
  1725. Xmyname=`basename $0`
  1726. XTMP=/tmp/${myname}$$
  1727. XUSER=${USER-`whoami`} || exit
  1728. X
  1729. Xusage() {
  1730. X    echo "Usage: $myname [-o /backup] filename" 1>&2
  1731. X    exit 1
  1732. X}
  1733. Xsorry() {
  1734. X    echo "Sorry, there are no online backups of $filename"
  1735. X    exit 1
  1736. X}
  1737. Xgiveup() {
  1738. X    echo "$myname: nothing changed"
  1739. X    exit 1
  1740. X}
  1741. X
  1742. Xset -- `getopt o: $*`
  1743. Xif [ $? != 0 ]; then
  1744. X    usage
  1745. Xfi
  1746. Xfor i in $*; do
  1747. X    case $i in
  1748. X    -o) BACKUP=$2; shift 2;;
  1749. X    --) shift; break;;
  1750. X    esac
  1751. Xdone
  1752. X
  1753. Xif [ $# != 1 ]; then
  1754. X    echo "$myname: missing filename" 1>&2
  1755. X    usage
  1756. Xfi
  1757. X
  1758. Xfilename=$1
  1759. Xcase $filename in
  1760. X/*) backdir=$BACKUP$1;;
  1761. X*)  backdir=$BACKUP`/bin/pwd`/$1;;
  1762. Xesac
  1763. X
  1764. Xtest -d $backdir || sorry
  1765. Xtrap "/bin/rm -f $TMP; exit" 0 1 2 15
  1766. X# We could ensure that the backup filenames look reasonable here...
  1767. Xls -lrt $backdir | awk "\$1 ~ /^-/ && \$3 == \"$USER\"" >$TMP
  1768. Xncopies=`sed -n '$=' $TMP`
  1769. Xcase $ncopies in
  1770. X1)
  1771. X    set -- `cat $TMP`
  1772. X    echo "There is only one backup of \"$filename\", dated $5 $6 $7"
  1773. X    echo -n "Retrieve this copy [y] ? "
  1774. X    read ans
  1775. X    case "$ans" in
  1776. X    "" | y* | Y* ) ;;
  1777. X    *) giveup;;
  1778. X    esac
  1779. X    ;;
  1780. X[0-9]*)
  1781. X    echo "There are $ncopies backup versions of \"$filename\" from:"
  1782. X    echo ""
  1783. X    awk '{printf "\t%2d.\t%s %2d %s\t(%d bytes)\n", NR,$5,$6,$7,$4}' $TMP
  1784. X    echo ""
  1785. X    while :; do
  1786. X        echo -n "Enter number corresponding to the version you want [$ncopies] "
  1787. X        read ans
  1788. X        case "$ans" in
  1789. X        "")
  1790. X            version=$ncopies
  1791. X            break;;
  1792. X        n* | N*)
  1793. X            giveup;;
  1794. X        [0-9]*)
  1795. X            if [ $ans -gt 0 -a $ans -le $ncopies ]; then
  1796. X                version=$ans
  1797. X                break
  1798. X            fi;;
  1799. X        esac
  1800. X        echo "Answer must be a number from 1 to $ncopies, or <cr>."
  1801. X    done
  1802. X    set -- `sed -n ${version}p $TMP`
  1803. X    ;;
  1804. X*)
  1805. X    sorry;;
  1806. Xesac
  1807. Xif [ -f $filename ]; then
  1808. X    owner=`ls -l $filename | awk '{print $3}'`
  1809. X    if [ $owner != $USER ]; then
  1810. X        echo "$myname: you are not the owner of $filename"
  1811. X        exit 1
  1812. X    fi
  1813. X    echo -n "\"$filename\" exists, overwrite [y] ? "
  1814. X    read ans
  1815. X    case "$ans" in
  1816. X    "" | y* | Y* );;
  1817. X    *) giveup;;
  1818. X    esac
  1819. Xfi
  1820. Xecho -n "Retrieving $5 $6 $7 version of \"$filename\" ... "
  1821. Xcp -p $backdir/$8 $filename || {
  1822. X    echo $myname: copy failed
  1823. X    exit 1
  1824. X}
  1825. X# Update the times, but keep the modes
  1826. Xtouch -f $filename || echo $myname: Can\'t touch \"$filename\"
  1827. Xecho "done!"
  1828. Xexit 0
  1829. END_OF_getback.sh
  1830. if test 2205 -ne `wc -c <getback.sh`; then
  1831.     echo shar: \"getback.sh\" unpacked with wrong size!
  1832. fi
  1833. chmod +x getback.sh
  1834. # end of overwriting check
  1835. fi
  1836. if test -f ilw.c -a "${1}" != "-c" ; then 
  1837.   echo shar: Will not over-write existing file \"ilw.c\"
  1838. else
  1839. echo shar: Extracting \"ilw.c\" \(3290 characters\)
  1840. sed "s/^X//" >ilw.c <<'END_OF_ilw.c'
  1841. X/*
  1842. X * i-list-walker -- apply a specified function to all inodes on a filesystem
  1843. X *
  1844. X * Based on original routines by Larry Philps.  This reimplementation is
  1845. X * Copyright 1988 by Rayan Zachariassen, solely to prevent you from selling
  1846. X * it or putting your name on it.  Free (gratis) redistribution is encouraged.
  1847. X */
  1848. X
  1849. X#include <stdio.h>
  1850. X#include <sys/param.h>
  1851. X#include <sys/types.h>
  1852. X#include <sys/file.h>
  1853. X#include <sys/time.h>
  1854. X#include <sys/vnode.h>
  1855. X#include <ufs/inode.h>
  1856. X#include <ufs/fs.h>
  1857. X
  1858. Xextern char *progname;        /* must be supplied by main program */
  1859. X
  1860. Xstatic char    errmsgbuf[30];
  1861. X#define    EMSG(x)    (errno < sys_nerr ? sys_errlist[x] : \
  1862. X           (sprintf(errmsgbuf, "unknown error %d", errno), errmsgbuf))
  1863. X
  1864. Xextern int    errno, sys_nerr;
  1865. Xextern char    *sys_errlist[];
  1866. Xextern off_t    lseek();
  1867. X
  1868. Xint
  1869. Xbread(fd, bno, buf, cnt)
  1870. X    daddr_t    bno;
  1871. X    char    *buf;
  1872. X    long    cnt;
  1873. X{
  1874. X    if (lseek(fd, (off_t) bno * DEV_BSIZE, L_SET) == -1) {
  1875. X        fprintf(stderr, "%s: bread: lseek: %s\n",
  1876. X                progname, EMSG(errno));
  1877. X        return -2;
  1878. X    }
  1879. X    if (read(fd, buf, (int) cnt) != cnt) {
  1880. X        fprintf(stderr, "%s: read error at block %u: %s\n",
  1881. X                progname, bno, EMSG(errno));
  1882. X        return -1;
  1883. X    }
  1884. X    return 0;
  1885. X}
  1886. X
  1887. Xint
  1888. Xbwrite(fd, bno, buf, cnt)
  1889. X    daddr_t    bno;
  1890. X    char    *buf;
  1891. X    long    cnt;
  1892. X{
  1893. X    if (lseek(fd, (off_t) bno * DEV_BSIZE, L_SET) == -1) {
  1894. X        fprintf(stderr, "%s: bwrite: lseek: %s\n",
  1895. X                progname, EMSG(errno));
  1896. X        return -2;
  1897. X    }
  1898. X    if (write(fd, buf, (int) cnt) != cnt) {
  1899. X        fprintf(stderr, "%s: write error at block %u: %s\n",
  1900. X                progname, bno, EMSG(errno));
  1901. X        return -1;
  1902. X    }
  1903. X    return 0;
  1904. X}
  1905. X
  1906. X/*
  1907. X * i-list-walker
  1908. X *
  1909. X * Opens the raw device indicated, and calls the function fn with an inode
  1910. X * pointer for every inode on the device.  If the function returns non-zero,
  1911. X * the inode on the disk is updated, unless the readonly flag is set.
  1912. X *
  1913. X * Return -1 in case of error, +ve if inodes were updated, 0 otherwise.
  1914. X */
  1915. X
  1916. Xilw(rawdev, fn, readonly)
  1917. X    char    *rawdev;
  1918. X    int    (*fn)();
  1919. X    int    readonly;
  1920. X{
  1921. X    register u_int    ino;
  1922. X    register int    fd, j, nfiles, changed, touched;
  1923. X    struct   dinode    *ip;
  1924. X    daddr_t        iblk;
  1925. X    struct dinode    itab[MAXBSIZE/sizeof(struct dinode)];
  1926. X    union {
  1927. X        struct fs u_sblock;
  1928. X        char dummy[SBSIZE];
  1929. X    } sb_un;
  1930. X#define sblock    sb_un.u_sblock
  1931. X
  1932. X    if ((fd = open(rawdev, readonly ? O_RDONLY : O_RDWR, 0)) < 0) {
  1933. X        fprintf(stderr, "%s: open(%s): %s\n",
  1934. X                progname, rawdev, EMSG(errno));
  1935. X        return -1;
  1936. X    }
  1937. X    /*
  1938. X     * Don't bother stat'ing the device, we might want to do this on
  1939. X     * real files some day.
  1940. X     */
  1941. X    /* printf ("starting filesystem %s\n", rawdev); */
  1942. X    sync();
  1943. X    if (bread(fd, SBLOCK, (char *)&sblock, (long) SBSIZE) < 0) {
  1944. X        close(fd);
  1945. X        return -1;
  1946. X    }
  1947. X    nfiles = sblock.fs_ipg * sblock.fs_ncg;
  1948. X    touched = 0;
  1949. X    for (ino = 0; ino < nfiles; touched += changed) {
  1950. X        iblk = fsbtodb(&sblock, itod(&sblock, ino));
  1951. X        if (bread(fd, iblk, (char *)itab, sblock.fs_bsize) < 0) {
  1952. X            (void) close(fd);
  1953. X            return -1;
  1954. X        }
  1955. X        changed = 0;
  1956. X        for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) {
  1957. X            ip = &itab[j];
  1958. X            if ((ino < ROOTINO) || ((ip->di_mode&IFMT) == 0))
  1959. X                continue;
  1960. X            if ((*fn)(ip, ino))
  1961. X                ++changed;
  1962. X        }
  1963. X        if (!changed)
  1964. X            continue;
  1965. X        if (readonly)
  1966. X            printf("replacing block %d with %d changes\n",
  1967. X                      iblk, changed);
  1968. X        else if (bwrite(fd, iblk, (char *)itab, sblock.fs_bsize) < 0) {
  1969. X            (void) close(fd);
  1970. X            return -1;
  1971. X        }
  1972. X    }
  1973. X    return touched;
  1974. X}
  1975. END_OF_ilw.c
  1976. if test 3290 -ne `wc -c <ilw.c`; then
  1977.     echo shar: \"ilw.c\" unpacked with wrong size!
  1978. fi
  1979. # end of overwriting check
  1980. fi
  1981. echo shar: End of shell archive.
  1982. exit 0
  1983.  
  1984.